﻿using System;
using Marvin.Categorization.NeuronalNetworks;
using MathNet.Numerics.LinearAlgebra.Double;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Marvin.Tests.Categorization.NeuronalNetwork
{
    [TestClass]
    public class TestBasicNeuronalNetworkFunctions
    {
        [TestMethod]
        public void TestLogicalAndFor0And1()
        {
            const uint firstNeuron = 0;
            const uint secondNeuron = 1;
            const uint expected = (uint)0; 

            Assert(firstNeuron, secondNeuron, expected);
        }

        [TestMethod]
        public void TestLogicalAndFor1And0()
        {
            const uint firstNeuron = 1;
            const uint secondNeuron = 0;
            const uint expected = (uint)0;

            Assert(firstNeuron, secondNeuron, expected);
        }

        [TestMethod]
        public void TestLogicalAndFor0And0()
        {
            const uint firstNeuron = 0;
            const uint secondNeuron = 0;
            const uint expected = (uint)0;

            Assert(firstNeuron, secondNeuron, expected);
        }


        [TestMethod]
        public void TestLogicalAndFor1And1()
        {
            const uint firstNeuron = 1;
            const uint secondNeuron = 1;
            const uint expected = (uint)1;

            Assert(firstNeuron, secondNeuron, expected);
        }

        [TestMethod]
        [ExpectedException(typeof (ArgumentException))]
        public void ThrowsAnExceptionIfRowCountIsWrong()
        {
            var logicalAnd = CreateLogicalAnd();

            NeuralNetworkBuilder
                .WithNumberOfInputNeurons(3)
                .WithOutputLayer(42, logicalAnd);

        }


        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ThrowsAnExceptionIfColumnCountIsWrong()
        {
            var logicalAnd = CreateLogicalAnd();

            NeuralNetworkBuilder
                .WithNumberOfInputNeurons(3)
                .WithHiddenLayer(42)
                .WithOutputLayer(2, logicalAnd);

        }
        private static void Assert(uint firstNeuron, uint secondNeuron, uint expected)
        {
            var logicalAnd = CreateLogicalAnd();

            var neuronalNetwork = NeuralNetworkBuilder
                .WithNumberOfInputNeurons(3)
                .WithOutputLayer(2, logicalAnd);

            uint result = neuronalNetwork.Predict(1, firstNeuron, secondNeuron);


            Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(expected, result);
        }

        private static DenseMatrix CreateLogicalAnd()
        {
            var logicalAnd = new DenseMatrix(2, 3);
            logicalAnd[0, 0] = 40;
            logicalAnd[0, 1] = -30;
            logicalAnd[0, 2] = -30;

            logicalAnd[1, 0] = -40;
            logicalAnd[1, 1] = 30;
            logicalAnd[1, 2] = 30;
            return logicalAnd;
        }
    }
}
